/*
 * @(#)QueryExecutionManager.java Jan 12, 2012 - 12:00:25 AM
 * 
 * Copyright 2011 Cin/UFPE/iSPEED
 */
package speed2010.reformulacao.query;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import speed2010.reformulacao.enums.EnrichmentType;
import speed2010.reformulacao.enums.ResultType;
import speed2010.reformulacao.enums.TemplateType;
import speed2010.reformulacao.useful.Standards;
import speed2010.reformulacao.useful.Utils;
import edu.stanford.smi.protege.model.Cls;
import edu.stanford.smi.protege.model.SimpleInstance;
import edu.stanford.smi.protegex.owl.model.RDFSClass;
import edu.stanford.smi.protegex.owl.model.impl.DefaultOWLIndividual;
import edu.stanford.smi.protegex.owl.model.impl.DefaultOWLNamedClass;
import edu.stanford.smi.protegex.owl.model.impl.DefaultRDFIndividual;
import edu.stanford.smi.protegex.owl.model.query.QueryResults;

/**
 * @author <a href="mailto:andreza.lba@gmail.com">Andreza Leite de Alencar</a>
 */
public class QueryExecutionManager {

	public QueryResult executeQuery(final Query query, final String piAddress) {
		final QuerySparql sparql = new QuerySparql();

		final String originalQuery = query.getOriginalQuery();
		final TemplateType template = query.getQueryTemplate();

		final byte[] destinyArray = query.getDestinyOntology();

		/*
		    byte[] sourceOntology = query.getSourceOntology();
			boolean containsOR = originalQuery.contains(Standards.OR_UNICODE);
			boolean containsAND = originalQuery.contains(Standards.AND_UNICODE);
			boolean containsNOT = originalQuery.contains(Standards.NOT_UNICODE);
		*/

		final ReformulationManager refoManager = new ReformulationManager();
		final Map<EnrichmentType, String> refoQueries = refoManager.reformulateQueryDL(query);

		if (refoQueries.containsKey(EnrichmentType.EXACT)) {
			refoQueries.remove(EnrichmentType.EXACT);
		}

		final Map<EnrichmentType, QueryResults> enrichedsMap = sparql.executeExactEnrichedQueries(refoQueries,
				destinyArray);
		final Map<ResultType, List<QueryResults>> queryResultsMap = new LinkedHashMap<ResultType, List<QueryResults>>();

		/*  
		 * Não precisa fazer a consulta para cada conceito, preservei a lógica do código original do Arruda
		 * Mas o trecho abaixo foi refatorado para ficar mais elegante
		 * 
			if (containsOR && !containsAND && !containsNOT) {
				String[] exactConcepts = originalQuery.split(Standards.OR_UNICODE);
		
				List<QueryResults> exactSourceResults = new ArrayList<QueryResults>();
				List<QueryResults> exactDestinyResults = new ArrayList<QueryResults>();
				for (String exactConcept : exactConcepts) {
					exactConcept = exactConcept.trim();
					QueryResults sourceExactResult = sparql.executeOriginalQuery(exactConcept, template, sourceOntology);
					if (sourceExactResult.hasNext()) {
						exactSourceResults.add(sourceExactResult);
					}
		
					QueryResults destinyExactResults = sparql.executeOriginalQuery(exactConcept, template, destinyArray);
					if (destinyExactResults.hasNext()) {
						exactDestinyResults.add(destinyExactResults);
					}
				}
				if (!exactSourceResults.isEmpty()) {
					queryResultsMap.put(ResultType.EXACT_SOURCE, exactSourceResults);
				}
				if (!exactDestinyResults.isEmpty()) {
					queryResultsMap.put(ResultType.EXACT_DESTINY, exactDestinyResults);
				}
			} else {
				QueryResults sourceResults = sparql.executeOriginalQuery(originalQuery, template, sourceOntology);
				if (sourceResults.hasNext()) {
					queryResultsMap.put(ResultType.SOURCE, Collections.singletonList(sourceResults));
				}
				if (!containsOR && !containsAND && !containsNOT) {
					QueryResults destinyExactResult = enricheds.get(EnrichmentType.EXACT);
					if (destinyExactResult.hasNext()) {
						List<QueryResults> destinyResults = Collections.singletonList(destinyExactResult);
						queryResultsMap.put(ResultType.DESTINY, destinyResults);
					}
				}
			}
		*/

		/*
		 * Se a ontologia de cluster já tem o merge de todas as ontologias do PD não é necessário fazer busca na ontologia de destino
		 * 
			List<QueryResults> exactSourceResults = new ArrayList<QueryResults>();
			QueryResults sourceExactResult = sparql.executeOriginalQuery(originalQuery, template, sourceOntology);
			if (sourceExactResult.hasNext()) {
				exactSourceResults.add(sourceExactResult);
			}
		
			if (!exactSourceResults.isEmpty()) {
				queryResultsMap.put(ResultType.EXACT_SOURCE, exactSourceResults);
			}
		*/

		final List<QueryResults> exactDestinyResults = new ArrayList<QueryResults>();
		final QueryResults destinyExactResults = sparql.executeOriginalQuery(originalQuery, template, destinyArray);
		if (destinyExactResults.hasNext()) {
			exactDestinyResults.add(destinyExactResults);
		}

		if (!exactDestinyResults.isEmpty()) {
			queryResultsMap.put(ResultType.EXACT_DESTINY, exactDestinyResults);
		}

		final Map<String, List<InstanceResult>> peerResults = processPeerResults(queryResultsMap, originalQuery,
				piAddress);
		final Map<String, List<InstanceResult>> enrichedResults = processEnrichedResults(enrichedsMap, refoQueries,
				originalQuery, piAddress);

		final QueryResult result = new QueryResult(peerResults, enrichedResults);

		return result;
	}

	private Map<String, List<InstanceResult>> processEnrichedResults(
			final Map<EnrichmentType, QueryResults> enrichedsMap, final Map<EnrichmentType, String> refoQueries,
			final String query, final String piAddress) {

		final Map<String, List<InstanceResult>> simpleResultMap = new LinkedHashMap<String, List<InstanceResult>>();

		for (final Entry<EnrichmentType, QueryResults> entry : enrichedsMap.entrySet()) {
			final QueryResults result = entry.getValue();
			final EnrichmentType enrichmentType = entry.getKey();
			final String refoQuery = refoQueries.get(enrichmentType);
			while (result.hasNext()) {
				@SuppressWarnings("unchecked")
				final Map<String, ?> nextResult = result.next();
				for (final Entry<String, ?> next : nextResult.entrySet()) {
					final boolean isOWLIndividual = next.getValue() instanceof DefaultOWLIndividual;
					final boolean isRDFIndividuall = next.getValue() instanceof DefaultRDFIndividual;
					if (isOWLIndividual || isRDFIndividuall) {
						final SimpleInstance individual = (SimpleInstance) next.getValue();

						final InstanceResult instance = buildInstanceEnriched(ResultType.ENRICHED, individual, query,
								piAddress, enrichmentType, refoQuery);

						Cls cls = null;
						final Collection<?> directTypes = individual.getDirectTypes();
						for (Object object : directTypes) {
							if (object instanceof DefaultOWLNamedClass) {
								DefaultOWLNamedClass type = (DefaultOWLNamedClass) object;

								if (!type.getLocalName().equals("NamedIndividual")) {
									cls = type;
								}
							}
						}

						List<String> enrichmentInfo = new ArrayList<String>();
						if (enrichmentType == EnrichmentType.SPECIALIZE) {
							if (cls.getDirectSuperclassCount() > 0) {
								for (Object sc : cls.getDirectSuperclasses()) {
									if (sc instanceof DefaultOWLNamedClass) {
										DefaultOWLNamedClass nc = (DefaultOWLNamedClass) sc;
										enrichmentInfo.addAll(buildEnrichmentInfo(instance, nc));
									}
								}
							}
						} else if (enrichmentType == EnrichmentType.GENERALIZE) {
							if (cls.getDirectSubclassCount() > 0) {
								for (Object sc : cls.getDirectSubclasses()) {
									if (sc instanceof DefaultOWLNamedClass) {
										DefaultOWLNamedClass nc = (DefaultOWLNamedClass) sc;
										enrichmentInfo.addAll(buildEnrichmentInfo(instance, nc));
									}
								}
							}
						} else if (enrichmentType == EnrichmentType.APPROXIMATE) {
							final Collection<Cls> directSuperclasses = cls.getDirectSuperclasses();
							final Cls ds = directSuperclasses.iterator().next();
							final DefaultOWLNamedClass directSuper = (DefaultOWLNamedClass) ds;

							for (Object dsc : directSuper.getDirectSubclasses()) {
								if (dsc instanceof DefaultOWLNamedClass) {
									DefaultOWLNamedClass nc = (DefaultOWLNamedClass) dsc;
									enrichmentInfo.addAll(buildEnrichmentInfo(instance, nc));
								}
							}
						}
						instance.setEnrichmentInfo(enrichmentInfo);

						final String conceptName = instance.getConceptName();
						if (!simpleResultMap.containsKey(conceptName)) {
							final List<InstanceResult> instanceList = new ArrayList<InstanceResult>();
							instanceList.add(instance);
							simpleResultMap.put(conceptName, instanceList);
						} else {
							final List<InstanceResult> instanceList = simpleResultMap.get(conceptName);
							instanceList.add(instance);
							simpleResultMap.put(conceptName, instanceList);
						}
					}
				}
			}
		}

		return simpleResultMap;
	}

	private InstanceResult buildInstanceEnriched(ResultType resultType, SimpleInstance instance, String originalQuery,
			String piAddress, EnrichmentType enrichmentType, final String refoQuery) {

		InstanceResult result = null;

		String rQuery = refoQuery.replaceAll("]", "");
		rQuery = rQuery.replaceAll("\\[", "");

		String instanceName = null;
		Collection<?> rdfTypes = null;
		String conceptName = null;
		Collection<?> comments = null;

		if (instance instanceof DefaultOWLIndividual) {
			final DefaultOWLIndividual individual = (DefaultOWLIndividual) instance;
			instanceName = individual.getName();
			rdfTypes = individual.getRDFTypes();
			conceptName = individual.getRDFType().getLocalName();
			comments = individual.getComments();
		} else {
			final DefaultRDFIndividual individual = (DefaultRDFIndividual) instance;
			instanceName = individual.getName();
			rdfTypes = individual.getRDFTypes();
			conceptName = individual.getRDFType().getLocalName();
			comments = individual.getComments();
		}

		if (rdfTypes.size() > 1) {
			for (final Object rdf : rdfTypes) {
				final RDFSClass rdfsClass = (RDFSClass) rdf;
				if (resultType == ResultType.ENRICHED) {
					final boolean containsOR = rQuery.contains(Standards.OR_UNICODE);
					final boolean containsAND = rQuery.contains(Standards.AND_UNICODE);
					if (containsOR && !containsAND) {
						final String[] conceptsArray = rQuery.split(Standards.OR_UNICODE);
						for (final String concept : conceptsArray) {
							if (concept.trim().equals(rdfsClass.getLocalName())) {
								conceptName = rdfsClass.getLocalName();
							}
						}
					} else if (!containsOR && containsAND) {
						final String[] conceptsArray = rQuery.split(Standards.AND_UNICODE);
						for (final String concept : conceptsArray) {
							if (concept.trim().equals(rdfsClass.getLocalName())) {
								conceptName = rdfsClass.getLocalName();
							}
						}
					} else if (!containsOR && !containsAND) {
						if (rQuery.trim().equals(rdfsClass.getLocalName())) {
							conceptName = rdfsClass.getLocalName();
						}
					}
				}
			}
		}

		String originalIP = "";
		if (comments != null) {
			if (!comments.isEmpty()) {
				for (final Object comment : comments) {
					if (comment.toString().contains("originalIP")) {
						originalIP = comment.toString().split("and")[1].trim();
					}
				}
			}
		}

		if (!originalIP.isEmpty()) {
			String sourceAddress = originalIP.split("=")[1].trim();
			if (sourceAddress.contains("]")) {
				sourceAddress = sourceAddress.replaceAll("]", "");
			} else if (sourceAddress.contains("[")) {
				sourceAddress = sourceAddress.replaceAll("[", "");
			}

			result = new InstanceResult(resultType, originalQuery, instanceName, conceptName, sourceAddress, "PD");
		} else {
			result = new InstanceResult(resultType, originalQuery, instanceName, conceptName, piAddress, "PI");
		}

		result.setEnrichedQuery(refoQuery);
		result.setEnrichmentType(enrichmentType);

		return result;
	}

	public List<String> buildEnrichmentInfo(InstanceResult instance, DefaultOWLNamedClass nc) {
		List<String> enrichmentInfo = new ArrayList<String>();

		final String query = instance.getOriginalQuery();
		final boolean containsOR = query.contains(Standards.OR_UNICODE);
		final boolean containsAND = query.contains(Standards.AND_UNICODE);

		String enrichmentPredicate = "";

		switch (instance.getEnrichmentType()) {
			case GENERALIZE : {
				enrichmentPredicate = " is SuperConcept of ";
			}
				break;

			case SPECIALIZE : {
				enrichmentPredicate = " is SubConcept of ";
			}
				break;

			case APPROXIMATE : {
				enrichmentPredicate = " is Close to ";
			}
				break;

			default :
				break;
		}

		if (containsOR && !containsAND) {
			String[] originalConcepts = query.split(Standards.OR_UNICODE);
			for (String originalConcept : originalConcepts) {
				if (nc.getLocalName().equals(originalConcept.trim())) {
					final String instanceConcept = instance.getConceptName();
					final String instanceValue = instance.getInstanceValue();
					final String x = instanceValue + " is Type of " + instanceConcept;
					final String y = instanceConcept + enrichmentPredicate + nc.getLocalName();

					enrichmentInfo.add(x);
					enrichmentInfo.add(y);
					break;
				}
			}
		} else if (!containsOR && containsAND) {
			String[] originalConcepts = query.split(Standards.AND_UNICODE);
			for (String originalConcept : originalConcepts) {
				if (nc.getLocalName().equals(originalConcept.trim())) {
					final String instanceConcept = instance.getConceptName();
					final String instanceValue = instance.getInstanceValue();
					final String x = instanceValue + " is Type of " + instanceConcept;
					final String y = instanceConcept + enrichmentPredicate + nc.getLocalName();

					enrichmentInfo.add(x);
					enrichmentInfo.add(y);
					break;
				}
			}
		} else if (!containsOR && !containsAND) {
			if (query.trim().contains(nc.getLocalName())) {
				final String instanceConcept = instance.getConceptName();
				final String instanceValue = instance.getInstanceValue();
				final String x = instanceValue + " is Type of " + instanceConcept;
				final String y = instanceConcept + enrichmentPredicate + nc.getLocalName();

				enrichmentInfo.add(x);
				enrichmentInfo.add(y);
			}

		}

		return enrichmentInfo;
	}

	private Map<String, List<InstanceResult>> processPeerResults(
			final Map<ResultType, List<QueryResults>> peersResultsMap, final String query, final String piAddress) {
		final Map<String, List<InstanceResult>> simpleResultMap = new LinkedHashMap<String, List<InstanceResult>>();

		for (final Entry<ResultType, List<QueryResults>> peersEntry : peersResultsMap.entrySet()) {
			final List<QueryResults> resultsList = peersEntry.getValue();
			final ResultType resultType = peersEntry.getKey();
			for (final QueryResults result : resultsList) {
				while (result.hasNext()) {
					@SuppressWarnings("unchecked")
					final Map<String, ?> nextResult = result.next();
					for (final Entry<String, ?> next : nextResult.entrySet()) {
						final boolean isOWLIndividual = next.getValue() instanceof DefaultOWLIndividual;
						final boolean isRDFIndividuall = next.getValue() instanceof DefaultRDFIndividual;
						if (isOWLIndividual || isRDFIndividuall) {
							final SimpleInstance individual = (SimpleInstance) next.getValue();

							final InstanceResult instance = buildInstanceExact(resultType, individual, query, piAddress);
							final String conceptName = instance.getConceptName();

							if (!simpleResultMap.containsKey(conceptName)) {
								final List<InstanceResult> instanceList = new ArrayList<InstanceResult>();
								instanceList.add(buildInstanceExact(resultType, individual, query, piAddress));
								simpleResultMap.put(conceptName, instanceList);
							} else {
								final List<InstanceResult> instanceList = simpleResultMap.get(conceptName);
								instanceList.add(buildInstanceExact(resultType, individual, query, piAddress));
								simpleResultMap.put(conceptName, instanceList);
							}
						}
					}
				}
			}
		}

		return simpleResultMap;
	}

	private InstanceResult buildInstanceExact(final ResultType resultType, final SimpleInstance instance,
			final String originalQuery, final String piAddress) {
		InstanceResult result = null;

		String instanceName = null;
		Collection<?> rdfTypes = null;
		String conceptName = null;
		Collection<?> comments = null;

		if (instance instanceof DefaultOWLIndividual) {
			final DefaultOWLIndividual individual = (DefaultOWLIndividual) instance;
			instanceName = individual.getName();
			rdfTypes = individual.getRDFTypes();
			conceptName = individual.getRDFType().getLocalName();
			comments = individual.getComments();
		} else {
			final DefaultRDFIndividual individual = (DefaultRDFIndividual) instance;
			instanceName = individual.getName();
			rdfTypes = individual.getRDFTypes();
			conceptName = individual.getRDFType().getLocalName();
			comments = individual.getComments();
		}

		if (rdfTypes.size() > 1) {
			for (final Object rdf : rdfTypes) {
				final RDFSClass rdfsClass = (RDFSClass) rdf;
				if (resultType == ResultType.EXACT_DESTINY) {
					final boolean containsOR = originalQuery.contains(Standards.OR_UNICODE);
					final boolean containsAND = originalQuery.contains(Standards.AND_UNICODE);
					if (containsOR && !containsAND) {
						final String[] conceptsArray = originalQuery.split(Standards.OR_UNICODE);
						for (final String concept : conceptsArray) {
							if (concept.trim().equals(rdfsClass.getLocalName())) {
								conceptName = rdfsClass.getLocalName();
							}
						}
					} else if (!containsOR && containsAND) {
						final String[] conceptsArray = originalQuery.split(Standards.AND_UNICODE);
						for (final String concept : conceptsArray) {
							if (concept.trim().equals(rdfsClass.getLocalName())) {
								conceptName = rdfsClass.getLocalName();
							}
						}
					} else if (!containsOR && !containsAND) {
						if (originalQuery.trim().equals(rdfsClass.getLocalName())) {
							conceptName = rdfsClass.getLocalName();
						}
					}
				}
			}
		}

		String originalIP = "";
		if (comments != null) {
			if (!comments.isEmpty()) {
				for (final Object comment : comments) {
					if (comment.toString().contains("originalIP")) {
						originalIP = comment.toString().split("and")[1].trim();
					}
				}
			}
		}

		if (!originalIP.isEmpty()) {
			String sourceAddress = originalIP.split("=")[1].trim();
			if (sourceAddress.contains("]")) {
				sourceAddress = sourceAddress.replaceAll("]", "");
			} else if (sourceAddress.contains("[")) {
				sourceAddress = sourceAddress.replaceAll("[", "");
			}

			result = new InstanceResult(resultType, originalQuery, instanceName, conceptName, sourceAddress, "PD");
		} else {
			result = new InstanceResult(resultType, originalQuery, instanceName, conceptName, piAddress, "PI");
		}

		return result;
	}

	public static void main(final String[] args) throws IOException {
		final String submittedQuery = "Student";// + Standards.OR_UNICODE + " Person";

		final List<EnrichmentType> ev = new ArrayList<EnrichmentType>();
		ev.add(EnrichmentType.SPECIALIZE);
//		ev.add(EnrichmentType.GENERALIZE);
		//		ev.add(EnrichmentType.APPROXIMATE);
//		ev.add(EnrichmentType.COMPOSE);

		final Query query = new Query(submittedQuery, ev);
		query.setSourceOntology(Utils.getBytesFromFile("NLO_EDU2.owl"));
		query.setCorrespondenceOntology(Utils.getBytesFromFile("NEDU-Alinhamento.owl"));
		query.setDestinyOntology(Utils.getBytesFromFile("NLO_EDU1.owl"));

		final QueryExecutionManager qm = new QueryExecutionManager();
		final QueryResult result = qm.executeQuery(query, "10.10.10.1");
		final Map<String, List<InstanceResult>> enrichedResults = result.getEnrichedResults();
		for (final Entry<String, List<InstanceResult>> entry : enrichedResults.entrySet()) {
			for (final InstanceResult ir : entry.getValue()) {
				System.out.println(ir.getEnrichmentType() + ":" + ir.getInstanceValue() + ":" + ir.getConceptName()
						+ ":" + ir.getEnrichedQuery());
				System.out.println(ir.getEnrichmentInfo().size());
			}
		}

		final Map<String, List<InstanceResult>> peerResults = result.getPeersResults();
		for (final Entry<String, List<InstanceResult>> entry : peerResults.entrySet()) {
			for (final InstanceResult ir : entry.getValue()) {
				System.out.println(ir.getResultType() + ":" + ir.getInstanceValue() + ":" + ir.getConceptName());
			}
		}

	}
}
